#ifndef	__BFX_REFXMLWRITER_H__
#define	__BFX_REFXMLWRITER_H__

namespace BFX
{

//////////////////////////////////////////////////////////////////////////////

class REFXMLTextEncoder;
#ifdef	BFX_DLL_BUILD
template class BFX_API REF<REFUniTextWriter>;
#endif

//////////////////////////////////////////////////////////////////////////////

// Represents a writer that provides fast non-cached forward-only way of generating XML streams containing XML documents 
// that conform to the W3C Extensible Markup Language (XML) 1.0 specification and the Namespaces in XML specification.
class BFX_API REFXMLWriter : public REFObject
{
public:
	// Specifies the state of the XmlWriter.
	enum WriteState
	{
		// Nothing has been written yet.
		WriteStateStart,
		// Writing the prolog.
		WriteStateProlog,
		// Writing a the start tag for an element.
		WriteStateElement,
		// Writing an attribute value.
		WriteStateAttribute,
		// Writing element content.
		WriteStateContent,
		// XmlWriter is closed; Close has been called.
		WriteStateClosed,
		// Writer is in error state.
		WriteStateError
	};

public:
	REFXMLWriter();
	virtual ~REFXMLWriter();

	// Write methods
	// Writes out the XML declaration with the version "1.0".
	virtual void WriteStartDocument() = 0;

	//Writes out the XML declaration with the version "1.0" and the speficied standalone attribute.
	virtual void WriteStartDocument(bool bStandalone) = 0;

	//Closes any open elements or attributes and puts the writer back in the Start state.
	virtual void WriteEndDocument() = 0;

	// Writes out the DOCTYPE declaration with the specified name and optional attributes.
	virtual void WriteDocType(const UniString& strName, const UniString& strPubid, const UniString& strSysid, const UniString& strSubset) = 0;

	// Writes out the specified start tag and associates it with the given namespace and prefix.
	virtual void WriteStartElement(const UniString& strPrefix, const UniString& strLocalName, const UniString& strNs) = 0;

	// Writes out a start tag with the specified local name with no namespace.
    void WriteStartElement(const UniString& strLocalName)
	{
		WriteStartElement(UniString(), strLocalName, UniString());
	}

	// Closes one element and pops the corresponding namespace scope.
	virtual void WriteEndElement() = 0;

	// Closes one element and pops the corresponding namespace scope. Writes out a full end element tag, e.g. </element>.
	virtual void WriteFullEndElement() = 0;

	// Writes out the attribute with the specified LocalName and value.
	void WriteAttributeString(const UniString& strLocalName, const UniString& strValue)
	{
		WriteStartAttribute(UniString(), strLocalName, UniString());
		WriteString(strValue);
		WriteEndAttribute();
	}
	// Writes out the attribute with the specified prefix, LocalName, NamespaceURI and value.
	void WriteAttributeString(const UniString& strPrefix, const UniString& strLocalName, const UniString& strNs, const UniString& strValue)
	{
		WriteStartAttribute(strPrefix, strLocalName, strNs);
		WriteString(strValue);
		WriteEndAttribute();
	}

	// Writes the start of an attribute.
	virtual void WriteStartAttribute(const UniString& strPrefix, const UniString& strLocalName, const UniString& strNs) = 0;

	// Closes the attribute opened by WriteStartAttribute call.
	virtual void WriteEndAttribute() = 0;

	// Writes out a <![CDATA[...]]>; block containing the specified text.
	virtual void WriteCData(const UniString& strText) = 0;

	// Writes out a comment <!--...-->; containing the specified text.
	virtual void WriteComment(const UniString& strText) = 0;

	// Writes out a processing instruction with a space between the name and text as follows: <?name text?>
	virtual void WriteProcessingInstruction(const UniString& strName, const UniString& strText) = 0;

	// Writes out an entity reference as follows: "&"+name+";".
	virtual void WriteEntityRef(const UniString& strName) = 0;

	// Forces the generation of a character entity for the specified Unicode character value.
	virtual void WriteCharEntity(UCHAR ch) = 0;

	// Writes out the given whitespace.
    virtual void WriteWhitespace(const UniString& strWs) = 0;

	// Writes out the specified text content.
	virtual void WriteString(const UniString& strText) = 0;

	// Returns the state of the XmlWriter.
	virtual WriteState GetWriteState() const = 0;

	// Closes the XmlWriter and the underlying stream/TextReader (if Settings.CloseOutput is true).
	virtual void Close() = 0;

	// Flushes data that is in the internal buffers into the underlying streams/TextReader and flushes the stream/TextReader.
	virtual void Flush() = 0;

private:
	// Constants
	static const int WriteNodeBufferSize = 1024;

	// Helper buffer for WriteNode
	UCHAR*	m_pWriteNodeBuffer;

};

// Represents a writer that provides fast non-cached forward-only way of generating XML streams 
// containing XML documents that conform to the W3CExtensible Markup Language (XML) 1.0 specification 
// and the Namespaces in XML specification.
class BFX_API REFXMLTextWriter : public REFXMLWriter
{
private:
	// State machine is working through autocomplete
	enum State
	{
		StateStart,
		StateProlog,
		StatePostDTD,
		StateElement,
		StateAttribute,
		StateContent,
		StateAttrOnly,
		StateEpilog,
		StateError,
		StateClosed
	};
	enum Token
	{
		TokenPI,
		TokenDoctype,
		TokenComment,
		TokenCData,
		TokenStartElement,
		TokenEndElement,
		TokenLongEndElement,
		TokenStartAttribute,
		TokenEndAttribute,
		TokenContent,
		TokenBase64,
		TokenRawData,
		TokenWhitespace,
		TokenEmpty
	};
	struct TagInfo : public REFObject
	{
		UniString	m_strName;
		UniString	m_strPrefix;
		UniString	m_strDefaultNs;
		REF<TagInfo>	m_pNext;
	};

public:
	REFXMLTextWriter(REFUniTextWriter* pWriter);
	virtual ~REFXMLTextWriter();

	//
	// Xml-Writer implementation
	//
	
	// Writes out the XML declaration with the version "1.0".
	virtual void WriteStartDocument()
	{
		StartDocument(-1);
	}

	// Writes out the XML declaration with the version "1.0" and the standalone attribute.
    virtual void WriteStartDocument(bool bStandalone)
	{
		StartDocument(bStandalone ? 1 : 0);
	}

	// Closes any open elements or attributes and puts the writer back in the Start state.
	virtual void WriteEndDocument();

	// Writes out the DOCTYPE declaration with the specified name and optional attributes.
	virtual void WriteDocType(const UniString& strName, const UniString& strPubid, const UniString& strSysid, const UniString& strSubset);

	// Writes out the specified start tag and associates it with the given namespace and prefix.
	virtual void WriteStartElement(const UniString& strPrefix, const UniString& strLocalName, const UniString& strNs);

	// Closes one element and pops the corresponding namespace scope.
	virtual void WriteEndElement();

	// Closes one element and pops the corresponding namespace scope.
	virtual void WriteFullEndElement();

	// Writes the start of an attribute.
    virtual void WriteStartAttribute(const UniString& strPrefix, const UniString& strLocalName, const UniString& strNs);

	// Closes the attribute opened by WriteStartAttribute.
	virtual void WriteEndAttribute();

	// Writes out a <![CDATA[...]]>; block containing the specified text.
	virtual void WriteCData(const UniString& strText);

	// Writes out a comment <!--...-->; containing the specified text.
	virtual void WriteComment(const UniString& strText);

	// Writes out a processing instruction with a space between the name and text as follows: <?name text?>
	virtual void WriteProcessingInstruction(const UniString& strName, const UniString& strText);

	// Writes out an entity reference as follows: "&"+name+";".
	virtual void WriteEntityRef(const UniString& strName);

	// Forces the generation of a character entity for the specified Unicode character value.
	virtual void WriteCharEntity(UCHAR ch);

	// Writes out the given whitespace.
    virtual void WriteWhitespace(const UniString& strWs);

	// Writes out the specified text content.
	virtual void WriteString(const UniString& strText);

	// Returns the state of the XmlWriter.
	virtual WriteState GetWriteState() const;

	// Closes the XmlWriter and the underlying stream/TextReader (if Settings.CloseOutput is true).
	virtual void Close();

	// Flushes data that is in the internal buffers into the underlying streams/TextReader and flushes the stream/TextReader.
	virtual void Flush();

	REFUniTextWriter* GetTextWriter() const;

protected:
	void StartDocument(int nStandalone);
	void WriteEndStartTag(bool bEmpty);
	void WriteEndAttributeQuote();
	void WriteEndElement(bool bLongFormat);
	void AutoComplete(Token token);
	void AutoCompleteAll();

	void PushStack();

	void WriteText(const UniString& str);
	void WriteText(UCHAR ch);

private:
	static const State s_pStateTableDefault[];
	static const State s_pStateTableDocument[];

	State		m_currentState;
	const State*	m_pStateTable;
	Token		m_lastToken;
	REF<REFUniTextWriter>	m_pWriter;
	REFXMLTextEncoder*	m_pXmlEncoder;

#ifdef	_MSC_VER
#pragma warning(push)
#pragma warning(disable:4251)
#endif	//	_MSC_VER
	// element stack
	REF<TagInfo>	m_pStack;
#ifdef	_MSC_VER
#pragma warning(pop)
#endif	//	_MSC_VER
};

}	//	namespace BFX

#endif	//	__BFX_REFXMLWRITER_H__
